package com.tyk.bookstore.back.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tyk.bookstore.back.common.core.model.result.CommonResultEnum;
import com.tyk.bookstore.back.common.core.model.type.BinVo;
import com.tyk.bookstore.back.common.core.model.type.SuggestionQuery;
import com.tyk.bookstore.back.common.core.util.AssertUtil;
import com.tyk.bookstore.back.common.core.util.IdGenerator;
import com.tyk.bookstore.back.common.core.util.ObjUtil;
import com.tyk.bookstore.back.common.security.util.PasswordUtil;
import com.tyk.bookstore.back.system.model.constant.UserConstant;
import com.tyk.bookstore.back.system.model.entity.RoleDo;
import com.tyk.bookstore.back.system.model.entity.UserDo;
import com.tyk.bookstore.back.system.model.query.UserAddQuery;
import com.tyk.bookstore.back.system.model.query.UserPageQuery;
import com.tyk.bookstore.back.system.model.query.UserUpdateQuery;
import com.tyk.bookstore.back.system.model.result.RoleResultEnum;
import com.tyk.bookstore.back.system.model.result.UserResultEnum;
import com.tyk.bookstore.back.system.model.type.RoleState;
import com.tyk.bookstore.back.system.model.vo.UserPageVo;
import com.tyk.bookstore.back.system.model.vo.UserVo;
import com.tyk.bookstore.back.system.repository.RoleRepository;
import com.tyk.bookstore.back.system.repository.UserRepository;
import com.tyk.bookstore.back.system.repository.UserRoleRepository;
import com.tyk.bookstore.back.system.service.UserService;
import com.tyk.bookstore.back.system.util.MapstructSystem;
import com.tyk.bookstore.back.system.util.UserUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Service
public class UserServiceImpl implements UserService {

    private final TransactionTemplate transactionTemplate;
    private final MapstructSystem mapstructSystem;
    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final UserRoleRepository userRoleRepository;

    @Override
    public UserPageVo getPage(UserPageQuery query) {
        Page<UserDo> page = new Page<>(query.getPage(), query.getLimit());
        LambdaQueryWrapper<UserDo> w = buildPageWrapper(query);
        userRepository.selectPage(page, w);
        return mapstructSystem.toUserPageVo(page);
    }


    @Override
    public UserVo getById(Long id) {
        UserVo vo = userRepository.selectGetUserByIdVo(id);
        AssertUtil.notNull(vo, UserResultEnum.USER_NOT_FOUND);
        return vo;
    }

    @Override
    public void addUser(UserAddQuery query) {

        // 手机号未绑定
        AssertUtil.isFalse(
                userRepository.exist1Eq(
                        UserDo::getPhone, query.getPhone(), UserDo.class
                ),
                UserResultEnum.PHONE_HAS_BOUND
        );

        // 邮箱未绑定
        AssertUtil.isFalse(
                userRepository.exist1Eq(
                        UserDo::getEmail, query.getEmail(), UserDo.class
                ),
                UserResultEnum.EMAIL_HAS_BOUND
        );


        // 角色ID列表存在
        AssertUtil.equal(
                roleRepository.count1In1Eq(
                        RoleDo::getId, query.getRoleIdList(),
                        RoleDo::getState, RoleState.ABLE,
                        RoleDo.class
                ),
                (long) query.getRoleIdList().size(),
                RoleResultEnum.ROLE_NOT_FOUND_OR_DISABLED
        );

        // 生成账号、密码加密、设置默认头像
        UserDo user = mapstructSystem.toUserDo(query);
        String account = UserUtil.account(ac -> userRepository.exist1Eq(
                UserDo::getAccount, ac, UserDo.class
        ));
        AssertUtil.notBlank(account, CommonResultEnum.NETWORK_ERROR);
        user.setAccount(account)
                .setPassword(PasswordUtil.encrypt(query.getPassword()))
                .setAvatar(UserConstant.USER_DEFAULT_AVATAR)
                .setId(IdGenerator.id());

        // 插入用户、用户-角色
        transactionTemplate.executeWithoutResult((status) -> {
            userRepository.insert(user);
            if (ObjUtil.notEmpty(query.getRoleIdList())) {
                userRoleRepository.insertBatchUserRole(user.getId(), query.getRoleIdList());
            }
        });
    }

    @Override
    public void updateById(UserUpdateQuery query) {

        // 用户存在
        AssertUtil.isTrue(
                userRepository.exist1Eq(
                        UserDo::getId, query.getId(), UserDo.class
                ),
                UserResultEnum.USER_NOT_FOUND
        );


        // 角色ID列表存在
        AssertUtil.equal(
                roleRepository.count1In1Eq(
                        RoleDo::getId, query.getRoleIdList(),
                        RoleDo::getState, RoleState.ABLE,
                        RoleDo.class
                ),
                (long) query.getRoleIdList().size(),
                RoleResultEnum.ROLE_NOT_FOUND_OR_DISABLED
        );

        // 更新用户表与用户-角色关联表
        UserDo userDo = mapstructSystem.toUserDo(query);
        transactionTemplate.executeWithoutResult(status -> {
            userRepository.updateById(userDo);
            userRoleRepository.deleteByUserId(query.getId());
            if (!query.getRoleIdList().isEmpty()) {
                userRoleRepository.insertBatchUserRole(query.getId(), query.getRoleIdList());
            }
        });

    }

    @Override
    public void deleteById(Long id) {

        // 用户存在
        AssertUtil.isTrue(
                userRepository.exist1Eq(
                        UserDo::getId, id, UserDo.class
                ),
                UserResultEnum.USER_NOT_FOUND
        );


        // 删除用户、用户-角色
        transactionTemplate.executeWithoutResult(status -> {
            userRepository.deleteById(id);
            userRoleRepository.deleteByUserId(id);
        });

    }

    @Override
    public List<BinVo> getSuggestionList(SuggestionQuery query) {
        return userRepository
                .<Long, String>selectBinList1Like(
                        UserDo::getId, UserDo::getName,
                        UserDo::getName, query.getKeyword(),
                        UserDo.class
                )
                .stream().map(it -> new BinVo(it.getV1(), it.getV2())).toList();
    }


    /**
     * 构建分页查询Wrapper
     *
     * @param query 参数
     * @return Wrapper
     */
    private LambdaQueryWrapper<UserDo> buildPageWrapper(UserPageQuery query) {

        LambdaQueryWrapper<UserDo> w = Wrappers.lambdaQuery(UserDo.class);

        w.like(ObjUtil.notBlank(query.getAccount()), UserDo::getAccount, query.getAccount());

        w.like(ObjUtil.notBlank(query.getPhone()), UserDo::getPhone, query.getPhone());

        w.like(ObjUtil.notBlank(query.getEmail()), UserDo::getEmail, query.getEmail());

        w.like(ObjUtil.notBlank(query.getName()), UserDo::getName, query.getName());

        w.eq(query.getGender() != null, UserDo::getGender, query.getGender());

        w.eq(query.getState() != null, UserDo::getState, query.getState());

        w.ge(query.getStartTime() != null, UserDo::getCreateTime, query.getStartTime());
        w.le(query.getEndTime() != null, UserDo::getCreateTime, query.getEndTime());

        w.orderBy(true, true, UserDo::getId);

        return w;

    }
}
